package com.example.backblog.service.fileupload.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.novelweb.tool.upload.local.pojo.UploadFileParam;
import com.example.backblog.dto.fileupload.AddFileDto;
import com.example.backblog.dto.fileupload.GetFileDto;
import com.example.backblog.entity.fileupload.Files;
import com.example.backblog.entity.user.User;
import com.example.backblog.repository.fileupload.FilesRepository;
import com.example.backblog.repository.user.UserRepository;
import com.example.backblog.service.fileupload.FileService;
import com.example.backblog.util.ErrorCodeUtil;
import com.example.backblog.util.MyLocalUpload;
import com.example.backblog.util.NovelWebUtils;
import com.example.backblog.util.TransferUtil;
import com.example.backblog.vo.ApiResult;
import com.example.backblog.vo.fileupload.UploadFileVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.example.backblog.util.ErrorCodeUtil.CHUNK_NON_EXIST;

@Service
@Slf4j
public class FileServiceImpl implements FileService {
    @Autowired
    private FilesRepository filesRepository;
    @Autowired
    private UserRepository userRepository;
    @Value("${file.save-path:/data-center/files/vip-file-manager}")
    private String savePath;
    @Value("${file.conf-path:/data-center/files/vip-file-manager/conf}")
    private String confFilePath;
    public String getFullSavePath(Files files,String targetPath) throws Exception {
        // 查找全路径
        String path = targetPath+File.separator;
        String parentPath = computeParentPath(files);
        path +=parentPath;
        return path;
    }


    private String computeParentPath(Files files) throws Exception {
        Long userId = files.getUserId();
        if (userId == null) {
            throw new Exception("非法路径");
        }
        // 查找用户
        Optional<User> userOp = userRepository.findById(userId);
        if (!userOp.isPresent()) {
            throw new Exception("非法路径");
        }
        User user = userOp.get();
        String userName = user.getName();
        String parentPath = "";
        Files tmp = files;
        while(tmp.getParentId()!=null){
            Optional<Files> byId = this.filesRepository.findById(tmp.getParentId());
            if (byId.isPresent()) {
                Files parent = byId.get();
                parentPath = parent.getFileName()+File.separator+parentPath;
                tmp = parent;
            }else{
                throw new Exception("非法路径");
            }
        }
        parentPath = userName + File.separator + parentPath;
        return parentPath;
    }

    public String getFullSavePath(Files files) throws Exception {
        // 查找全路径
        String path = files.getFileName();
        String parentPath = computeParentPath(files);
        path  = parentPath+path;
        return path;
    }
    public String getFullSavePath(Long parentId,String userName) throws Exception {
        // 查找全路径
        String path = "";
        String parentPath = "";
        Long tmp = parentId;
        while(tmp!=null){
            Optional<Files> byId = this.filesRepository.findById(tmp);
            if (byId.isPresent()) {
                Files parent = byId.get();
                parentPath = parent.getFileName()+File.separator+parentPath;
                tmp = parent.getParentId();
            }else{
                throw new Exception("非法路径");
            }
        }
        parentPath = userName + File.separator+parentPath;
        path +=parentPath;
        return path;
    }
    @Override
    public InputStream getFileInputStream(Long id) {
        try {
            Files files = filesRepository.findById(id).get();
            File file = new File(savePath + File.separator + files.getFilePath());
            return new FileInputStream(file);
        } catch (Exception e) {
            log.error("获取文件输入流出错", e);
        }
        return null;
    }

    @Override
    public ApiResult<Files> getFileDetails(Long id) {
        Files files = filesRepository.findById(id).get();
        return ApiResult.success(files);
    }

    @Override
    public ApiResult<Object> checkFileMd5(String md5, String fileName, Long parentId, Long userId) {
        try {
            // 查询对应的conf或者tmp以及目标文件是否存在，
            // 如果conf以及tmp存在，则文件上传到一半
            // 如果conf以及目标文件存在，文件上传成功
            // 其他文件不存在
            String tmpName = fileName;
            // 查询用户
            Optional<User> userOptional = userRepository.findById(userId);
            if (!userOptional.isPresent()) {
                throw new Exception("文件出错");
            }
            String parentPath = getFullSavePath(parentId,userOptional.get().getName());
            // 创建目录
            createDirector(confFilePath,parentPath);
            createDirector(savePath,parentPath);

            fileName = parentPath+File.separator+tmpName;
            cn.novelweb.tool.http.Result result = MyLocalUpload.checkFileMd5(md5, fileName, confFilePath, savePath);
            return NovelWebUtils.forReturn(result);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return ApiResult.error(50001,"上传失败", "上传失败");
    }

    private void createDirector(String confFilePath, String parentPath) {
        File file = new File(confFilePath + File.separator + parentPath);
        if (!file.exists()) {
            FileUtil.mkdir(file);
        }
    }

    @Override
    public ApiResult<Object> breakpointResumeUpload(UploadFileParam param, Long parentId, HttpServletRequest request, Long userId) {
        try {
            String tmpName = param.getName();
            // 查询用户
            Optional<User> userOptional = userRepository.findById(userId);
            if (!userOptional.isPresent()) {
                throw new Exception("文件出错");
            }
            String parentPath = getFullSavePath(parentId,userOptional.get().getName());
            param.setName(parentPath+File.separator+tmpName);
            createDirector(confFilePath,parentPath);
            createDirector(savePath,parentPath);
            // 这里的 chunkSize(分片大小) 要与前端传过来的大小一致
            cn.novelweb.tool.http.Result result = MyLocalUpload.fragmentFileUploader(param, confFilePath, savePath, 5242880L, request);
            // 判断是否上传完成
            if (result.getCode().equals(String.valueOf(HttpStatus.CREATED.value()))) {
                // 上传完成
                Files files = new Files();
                files.setFileName(tmpName);
                files.setCreatedTime(LocalDateTime.now());
                // 计算文件大小
                files.setSize((int) param.getSize());
                files.setType(0);
                files.setParentId(parentId);
                files.setUserId(userId);
                filesRepository.save(files);
            }else if(result.getCode().equals(String.valueOf(HttpStatus.CREATED.value()))){

            }
            return NovelWebUtils.forReturn(result);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return ApiResult.error(ErrorCodeUtil.FILE_UPLOAD, "上传失败",null);
    }

    @Override
    public ApiResult<String> addFile(AddFileDto dto, Long userId) {
        Files file = new Files();
        TransferUtil.transfer(dto, file);
        file.setUserId(userId);
        if (filesRepository.existsByFileNameEquals(dto.getFileName())) {
            return ApiResult.success( "添加成功");
        }

        filesRepository.save(file);
        return ApiResult.success( "添加成功");
//        filesMapper.insert(file.setFilePath(dto.getFileName()));
//        return Results.newSuccessResult(null, "添加成功");
    }

    @Override
    public ApiResult<List<GetFileDto>> getFileList(Integer pageNo, Integer pageSize) throws InstantiationException, IllegalAccessException {
        Page<Files> all = this.filesRepository.findAll(PageRequest.of(pageNo, pageSize));
        List<Files> content = all.getContent();
        List<GetFileDto> getFileDtos = TransferUtil.transferList(content, GetFileDto.class);
        return ApiResult.success(getFileDtos);
    }

    @Override
    public ApiResult<Object> checkFileChunk(UploadFileParam param, Long parentId, HttpServletRequest request, Long userId) {

        try {
            String tmp = param.getName();
            // 查询用户
            Optional<User> userOptional = userRepository.findById(userId);
            if (!userOptional.isPresent()) {
                throw new Exception("文件出错");
            }
            String fullSavePath = getFullSavePath(parentId,userOptional.get().getName());
            param.setName(fullSavePath+File.separator+tmp);
            File confFile = FileUtil.file(FileUtil.mkdir(confFilePath), String.format("%s.conf", param.getName()));
            if (confFile.exists()) {
                // 查询目标文件是否存在
//                File file = new File(savePath + File.separatorChar + param.getName());
                boolean isFileEmpty = (new File(savePath + File.separatorChar + param.getName())).exists();
                if(isFileEmpty){
                    UploadFileVo uploadFileVo = UploadFileVo.builder().complete(true).build();
                    return ApiResult.success(uploadFileVo);
                }
                List<Integer> chunksIndex = new ArrayList<>();
                byte[] completeList = FileUtils.readFileToByteArray(confFile);
                for (int i = 0; i < completeList.length; i++) {
                    if (completeList[i]==127) {
                        chunksIndex.add(i);
                    }
                }
                return ApiResult.success(UploadFileVo.builder().complete(false).completeChunks(chunksIndex).build());
            }else{
                return ApiResult.error(CHUNK_NON_EXIST,"文件快不存在",UploadFileVo.builder().complete(false)
                        .build());
            }

        } catch (IOException var18) {
            var18.printStackTrace();
            return ApiResult.error(CHUNK_NON_EXIST,"文件快不存在",UploadFileVo.builder().complete(false)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    @Transactional
    public void deleteFile(Long id) throws Exception {
        if(this.filesRepository.existsByParentIdEquals(id)){
            throw new Exception("存在子文件");
        }
        Optional<Files> byId = filesRepository.findById(id);
        Files files = byId.get();
        filesRepository.deleteById(id);
        // 查询用户
        Optional<User> userOptional = userRepository.findById(files.getUserId());
        if (!userOptional.isPresent()) {
            throw new Exception("文件出错");
        }
        // 获取父目录
        Long parentId = files.getParentId();
        String fullSavePath = getFullSavePath(parentId,userOptional.get().getName());

        // 查询是否为目录，如果为目录递归删除文件
        //清理文件夹
        List<Files> allFiles = filesRepository.findAllByParentIdEquals(parentId);
        List<String> fileNames = allFiles.stream().map(Files::getFileName).collect(Collectors.toList());

        cleanFile(fileNames,fullSavePath);
    }

    @Override
    public void download(Long id, HttpServletResponse response) throws Exception {
        Optional<Files> op = this.filesRepository.findById(id);
        if (op.isPresent()) {
            Files files = op.get();
            String fileName = files.getFileName();
            String fullSavePath = getFullSavePath(files, savePath);
            String fullPath =  fullSavePath;
            File file = new File(fullPath+File.separator+fileName);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            response.setContentType("application/octet-stream;charset=ISO8859-1");
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            // 设置长度
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedInputStream inputStream = FileUtil.getInputStream(file);
            IoUtil.copy(inputStream,outputStream);
            response.addHeader("Content-Length",String.valueOf(file.length()));

            outputStream.flush();

        }else {
            throw new Exception("文件不存在");
        }
    }

    @Override
    public List<Files> findAll(Long parentId, Long userId) {
        List<Files> files = null;
        if (parentId == null) {
            // 查找顶级文件
            files = this.filesRepository.findAllByParentIdIsNullAndUserIdEqualsOrderByDirectorDesc(userId);
        }else {
            files = this.filesRepository.findAllByParentIdEqualsAndUserIdEqualsOrderByDirectorDesc(parentId,userId);
        }
        return files;
    }

    @Override
    public void viewMedia(Long id, HttpServletResponse response) throws Exception {
        Optional<Files> op = this.filesRepository.findById(id);
        if (op.isPresent()) {
            Files files = op.get();
            String fileName = files.getFileName();
            String fullSavePath = getFullSavePath(files, savePath);
            String fullPath =  fullSavePath;
            File file = new File(fullPath+File.separator+fileName);
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
//            response.setContentType("application/octet-stream;charset=ISO8859-1");
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            response.setHeader("content-type","application/msword");
            // 设置长度
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedInputStream inputStream = FileUtil.getInputStream(file);
            IoUtil.copy(inputStream,outputStream);
            response.addHeader("Content-Length",String.valueOf(file.length()));

            outputStream.flush();

        }else {
            throw new Exception("文件不存在");
        }
    }

    @Override
    public Object findOne(Long id) throws Exception {
        Files files = this.filesRepository.findById(id).get();
        String fullSavePath = getFullSavePath(files);
        files.setFilePath(fullSavePath);
        return files;

    }

    private void cleanFile(List<String> fileNames, String fullSavePath) {
        System.out.println(fileNames);
        List<String> saveFiles = FileUtil.listFileNames(savePath+File.separator+fullSavePath);
        for (String saveFile : saveFiles) {
            String[] split = saveFile.split("_");
            if(split.length==2 && split[1].equals("tmp")){
            }else{
                // 查询是否存在fileName中
                if(!fileNames.contains(saveFile)){
                    // 删除对应文件
                    FileUtil.del(savePath+File.separator+fullSavePath+FileUtil.FILE_SEPARATOR+saveFile);
                    // 同时删除对应的conf文件
                    FileUtil.del(confFilePath+File.separator+fullSavePath+FileUtil.FILE_SEPARATOR+saveFile+".conf");
                }
            }
        }


    }
}
